ci(stale): correct the action SHA the job has never once resolved - #8463
ci(stale): correct the action SHA the job has never once resolved#8463baozhoutao wants to merge 1 commit into
Conversation
…jectui#8126)
`stale.yml` pinned `actions/stale@e00e804f6792d3fedb5bd3a27df2761c5f86c981`.
That SHA is not v9.0.0's commit and is not any commit in `actions/stale`, so
every run died in `Set up job` before the action started:
##[error]Unable to resolve action `actions/stale@e00e804f6792d3fedb5bd3a27df2761c5f86c981`,
unable to find version `e00e804f6792d3fedb5bd3a27df2761c5f86c981`
read verbatim from run 34172858995. Re-derived 2026-09-08: 236 completed
`schedule` runs, run #1 (21052154491, 2026-01-16T01:14:37Z) and run #236 both
`failure`. The comment beside the ref was right; only the SHA was wrong.
The replacement is measured, not looked up. A probe branch resolved the tag on
a real runner and the runner printed the mapping:
Download action repository 'actions/stale@v9.0.0' (SHA:28ca1036281a5e5922ead5184a1bbf96e5fc984e)
Ten candidate refs were probed in one run (34173534757): `actions/checkout@v7`
green as the positive control, the pinned SHA red as the negative control,
`main` / `master` / v8 / v9 / v9.0.0 / v10 / v11 all resolving, v12 not. v9.0.0
is kept because it is the version this file already declares; a bump is a
separate decision.
Then measured what turning it on does, before turning it on. This file with
only the SHA changed, plus `debug-only: true` and a read-only token, ran GREEN
(34173608021) -- the first success in this workflow's life -- and swept the
open board to exhaustion: 466 items (457 issues, 9 PRs) and a `Statistics:`
block with no stale, label or comment counter at all. Nothing marked, nothing
closed. Control on the same file the same day (34173673214), thresholds
dropped to 0: `New stale items: 125`, `Added items labels: 125`, `Added items
comments: 125`. The zero is a reading, not a silent instrument. It is zero
because the board is young -- oldest last-touch among 457 open issues is #5157
at 2026-08-21T08:18:51Z, 18 days against a 60-day threshold.
One line of behaviour changes: the SHA. Policy, permissions, cron and the
objectui#7956 accept-360 comment block are untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
⛔ THIS BRANCH IS EVIDENCE, NOT A CLAIM. It is not a second seat working objectui#8126 and it must never be merged. The card's claim and its fix live on `claude/issue-8126-stale-yml` (PR #8463). The three dispatch-only runs it produced, all dry runs that wrote nothing (`debug-only: true`, read-only token): 34173534757 ten candidate action refs, one job each, with a positive and a negative control 34173608021 the real policy with the corrected SHA — first green run in this workflow's life; 466 open items swept, 0 marked 34173673214 positive control for that zero: thresholds at 0 report `New stale items: 125` `stale.yml` here is now byte-identical to origin/main again, so nothing can be dispatched off this branch by accident. The remote ref cannot be deleted from an agent container (AGENTS.md: ref deletions are refused with HTTP 403 and git still prints `Everything up-to-date`), which is why this commit exists instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
|
⛔ HELD in draft by the The root cause is proven, the replacement SHA was obtained by having a runner print the mapping rather than by hand, and both controls fire in the same probe run ( Why it is not being flipped ready: the file that was measured is not the file that lands. The probes carried ⇒ Two declared contracts conflict, so this seat acts on the stricter one and routes rather than deciding. objectui#8126 now carries Nothing is owed by this PR while it waits — no rebase, no changes requested. When the answer comes back as A or C this goes straight to CI → ready → queue; if it comes back as B, this PR is superseded and closes unmerged at zero rollback cost, which the report already anticipated. Generated by Claude Code |
Fixes #8126
One line of behaviour changes: the pinned SHA. Everything else in this diff is the comment that records why.
The real cause, read from the log rather than guessed
The card named the pinned action ref as the first place to look and explicitly did not claim it was the cause. It is. Read verbatim from run 34172858995:
e00e804f…is notactions/stale's v9.0.0 commit and is not any commit in that repository. The comment beside it (# v9.0.0) was right all along — the version exists, the SHA was fiction. The job never reachedactions/stale; the 1-4 seconds in the card measure how fast it fails to begin.Re-derived on today's tree, and the instrument checked first
scheduleruns21052154491, 2026-01-16T01:14:37Z, failure34172858995, 2026-09-08T00:17:51Z, failure?status=successread, withlabeler.yml= 5448 as its control.statusbut notconclusion, so I could not re-run that exact filter myself — what I add instead is that the first and the last run both failed and that the ref is provably unresolvable todaygit log -Scannot date the bad line. The oldest commit this checkout can reach forstale.ymlis the shallow boundary (9969e9f63, in.git/shallow, parent unknown), so it reads as the file's creation and is not — the run history proves the file predates it by six months.The replacement SHA is measured, not looked up
actions/staleis not reachable from this session's API surface, so guessing a SHA would have reproduced the exact defect one layer up. Instead, ten candidate refs were probed as ten independent jobs in one run (34173534757) on a throwaway branch, every one of them a dry run (debug-only: true,days-before-*: -1, read-only token):Set up jobactions/checkout@v7— positive controlactions/stale@e00e804f…— negative control, the ref onmainactions/stale@main,@master,@v8,@v9,@v9.0.0,@v10,@v11actions/stale@v12The runner printed the mapping itself, which is where the new SHA comes from:
v9.0.0 is kept because it is the version this file already declares. A version bump is a separate decision, not something to smuggle in behind a repair.
What turning it on does — measured before turning it on
This file with only the SHA changed, plus
debug-only: trueand a read-only token, ran green: 34173608021 — the first successful run in this workflow's life. It swept the open board to exhaustion (No more issues found to process):No stale counter, no label counter, no comment counter. Nothing would be marked and nothing would be closed.
A zero is worthless without a control, so the same file was run again with the thresholds dropped to
0(34173673214) — the block gains exactly the lines that were absent:So the zero is a reading off a working instrument. It is zero because the board is young: the oldest last-touch among 457 open issues is #5157 at 2026-08-21T08:18:51Z — 18 days, against a 60-day threshold. The card's worry that a fix would turn on "eight months of accumulated sweeping in one scheduled run" is falsified: there is no accumulation. This is the cheapest possible moment to land the repair.
pm:queue,pm:on-holdandneeds-user-decisionare not exempt, and those are the cards parked on purpose waiting for a human. Three of the five oldest-touched open issues carry onlypm:queue+domain:ui. The first quiet 60-day stretch on the board is when this job starts closing the maintainer's own decision queue. That question is not this diff's to answer — see the recommendation below.Verification
pnpm exec vitest run scripts/__tests__/ci-cd-pipeline-doc.test.ts scripts/__tests__/workflow-cache-save-bound.test.tsTest Files 2 passed (2)·Tests 60 passed (60)node scripts/check-changeset-presence.mjspnpm check:control-bytesnode scripts/check-shell-escape-residue.mjsnode scripts/check-governed-queue-guard.mjs --test .github/workflows/stale.ymlThe two workflow-shape pins are the ones that could have moved:
ci-cd-pipeline-docrequiresstale.ymlto keep a heading on the CI/CD page (it does — untouched), andworkflow-cache-save-boundrequires this job to keep notimeout-minutesand to keep360andobjectui#7956in its comments (all three preserved; the added comment sits above thesteps:entry, not in the accept-360 block).Permissions, cron, both message bodies, both exempt sets, all four day thresholds and
operations-per-runare byte-identical tomain.⭐ Recommendation the maintainer has to route: retire this workflow
Not implemented here — retiring it removes a declared capability, which is not a dev seat's call. The evidence, all measured above:
Retirement is not free and the costs should be named: deleting the file also requires deleting its section and inventory row on
content/docs/guide/ci-cd-pipeline.md(the doc-parity pin fails in both directions) and its entry in the accept-360 table ofscripts/__tests__/workflow-cache-save-bound.test.ts. AGENTS.md also records that the Actions-registry behaviour on deleting a workflow from the default branch has never happened in this repository and is untested — so the registry entry's fate is unknown, not predicted.If retirement is chosen, this PR is superseded and can simply be closed unmerged. If it is not, this PR is the repair, and the exempt-label question becomes live within roughly six weeks.
四轴分析
方案 A —— 只修 SHA(本 PR) · 方案 B —— 退役
stale.yml· 方案 C —— 修 SHA 并同时改写豁免标签集,把pm:*纳入 exempt实际业务需求。 实测而非"读起来像有用":该 job 自 run #1 起从未执行过一次,236 次全红;按真实策略实跑,处理 466 条开放条目、标记 0 条(阳性对照同一文件、阈值置 0 时报 125 条,证明读数来自可用仪器)。没有任何东西消费它的输出——它不是 required check、不阻塞任何合并。它服务的场景(自动清理外部贡献者遗弃的 issue/PR)在本仓不存在:测量到的整个开放集合的作者都是仓库成员。⇒ 这一轴指向 B。A 修好的是一台没人用的机器;C 在没人用的机器上再投入策略设计。
项目长远合理性。 本仓反复处理的正是"看起来像强制、其实不是"这一类(#3009 / #3181 / #3494),而这张卡是同一类的极端形态:声明了一套自动化,八个月一次也没跑。contract-first 的处理只有两条——要么真正兑现声明,要么删掉声明;保留一个红着的声明是两者中最差的第三条。A 兑现了声明,但兑现的是一套没人要的声明;B 删掉声明,与 ADR-0049 enforce-or-remove 的方向一致。C 是"先补丁再说":它在能力本身未被确认需要之前就去调策略参数,属于临时补丁式选项,长期代价是把一个无消费能力固化成需要长期维护的策略面。⇒ B 优,A 次,C 最差。
防 AI 写代码犯错(尤其防 AI 写元数据 app 犯错)。 这一轴上本卡有一条直接教训:
e00e804f…这个 SHA 就是一个声明了、运行时不兑现的 pin——它看起来是最严格的写法(SHA pin 优于浮动 tag),实际上是本仓唯一一个 SHA pin,也是唯一一个从未解析成功过的 action ref;仓内其余每一个 action 都用浮动 major tag(actions/checkout@v7等),从不出事。⇒ 收紧的形式如果没有任何东西验证它,就只是更精致的幻影。本 PR 因此不接受"手写一个 SHA"这条路,而是让 runner 自己打印 tag→SHA 映射再抄回来,并在注释里把这条禁令写死。就方案而言:B 让这类错误结构上不可能再犯(文件不存在);A 保留了这个面但把它变成可验证的;C 新增策略参数 = 新增可写错的面。⇒ B 优于 A 优于 C。创业阶段不扩散需求。 维护者 2026-08-04:「我们是一个创业项目,应该先专注于核心能力」。一个八个月零产出、零消费者的能力,正是"已发布零消费的能力不因沉没成本获得豁免"所指的对象——它已经写了八个月,这不构成保留理由。维护者 2026-08-27 逐字裁:「项目在创业阶段,用户也很少,短期不考虑渐进。」⇒ 退役默认立即,不设分阶段窗口;C 恰恰是分阶段过渡的形态(先留着、慢慢调策略),按这条裁决除非有具名外部用户证据否则不得作为推荐,而本卡测得的证据方向相反:整个开放集合无外部作者。⇒ 强指向 B,明确反对 C。
推荐:B(退役),A 作为已落地的安全中间态。 四轴一致指向 B,无冲突需要权衡。之所以仍然提交 A:①卡片点名的缺陷是真实的,修复只有一行且经真实 runner 验证;②A 今天的实测影响恰好为 0,是代价最低的时刻;③把一个长期红着的定时任务变绿,消除的是"定时任务失败可以忽略"这种被训练出来的麻木;④若维护者裁 B,A 只需关掉 PR 不合并即可,零回滚成本。⛔ 本席位不执行 B——删除已声明能力属于维护者决定,按 dispatch 交回本座位路由。
顺带记录、未单独立卡(路由权按 dispatch 保留给派发席位): ① v9.0.0 面向 Node 20,runner 已打印
Node.js 20 is deprecated … being forced to run on Node.js 24;@v11在同一次探测中解析成功且无此告警。是否升版应在退役决定之后再谈,否则是为一个可能被删掉的文件做迁移。② 本仓唯一的 SHA pin 就是这一个,其余 action 一律浮动 major tag——pin 策略在本仓并不统一,这本身是一个可以裁的问题。Generated by Claude Code